#include "NES.h"
#include "mainwindow.h"




static void ErrorCallabck(void* v,const char* ErrStr){

  QMetaObject::invokeMethod((MainWindow*)((NES*)v)->mainwindow,"AudioDeviceError",Qt::ConnectionType::QueuedConnection,Q_ARG(QString,QString(ErrStr)));
}




NES::NES(H_NES_CPP::SampleRate SampleRate,int NumOfSampleBit,void* mainwindow):H_NES_CPP(SampleRate){

  this->FrameRadioMult=1;
  this->Bitmap=new uint32_t[256*240];
  this->palette=NES_Palette::Default;

  this->mainwindow=mainwindow;
  this->SampleRate=SampleRate;
  this->NumOfSampleBit=NumOfSampleBit;
  audioOutputLock=new std::mutex();
  videoOutputLock=new std::mutex();

  AudioBuffer=nullptr;
  AudioBufferLength=0;

  ((MainWindow*)mainwindow)->audioOutput->Close();
  ((MainWindow*)mainwindow)->audioOutput->SetBuffer(((int)SampleRate/1000)*8*8,8);
  ((MainWindow*)mainwindow)->audioOutput->SetSampleParam((int)SampleRate,NumOfSampleBit);
  AudioOpen();

}

NES::~NES(){

  if(AudioBufferLength>0){
    delete[] AudioBuffer;
  }

  delete audioOutputLock;

}

int NES::SetSampleRate(H_NES_CPP::SampleRate SampleRate){

  this->SampleRate=SampleRate;

  return SampleRate_NumOfBit_Update();
}

int NES::SetNumOfSampleBit(int NumOfSampleBit){

  this->NumOfSampleBit=NumOfSampleBit;

  return SampleRate_NumOfBit_Update();
}

int NES::SetFormat(H_NES_CPP::SampleRate SampleRate,int NumOfSampleBit){

  this->SampleRate=SampleRate;
  this->NumOfSampleBit=NumOfSampleBit;

  return SampleRate_NumOfBit_Update();
}

void NES::SetPalette(NES_Palette* palette){
  videoOutputLock->lock();
  this->palette=palette;
  videoOutputLock->unlock();
}

NES_Palette* NES::GetPalette(){
  return palette;
}

int NES::AudioOpen(){
  return ((MainWindow*)mainwindow)->audioOutput->Open(ErrorCallabck,this);
}

int NES::SampleRate_NumOfBit_Update(){
  int r;
  MainWindow* mainwindow;
  
  mainwindow=(MainWindow*)this->mainwindow;

  ((MainWindow*)mainwindow)->audioOutput->Close();
  SetOutputSampleRate(SampleRate);
  audioOutputLock->lock();
  ((MainWindow*)mainwindow)->audioOutput->SetBuffer(((int)SampleRate/1000)*8*8,8);
  mainwindow->audioOutput->SetSampleParam((int)SampleRate,NumOfSampleBit);
  audioOutputLock->unlock();
  r=AudioOpen();

  return r;
}

void NES::AudioDataOutput_8(void* AudioSampleArray,int Length){
  unsigned char* dst;
  unsigned char* src;
  int i;

  Length/=4;

  if(AudioBufferLength<Length){
    AudioBufferLength=Length;
    if(AudioBuffer!=nullptr){
      delete[] AudioBuffer;
    }
    AudioBuffer=new unsigned char[Length];
  }

  dst=(unsigned char*)AudioBuffer;
  src=(unsigned char*)AudioSampleArray;

  for(i=0;i<Length;i++){
    dst[i]=src[4*i+3];
  }
  
  ((MainWindow*)mainwindow)->audioOutput->Input(dst,Length);
}

void NES::AudioDataOutput_16(void* AudioSampleArray,int Length){
  unsigned char* dst;
  unsigned char* src;
  int i;

  Length/=2;

  if(AudioBufferLength<Length){
    AudioBufferLength=Length;
    if(AudioBuffer!=nullptr){
      delete[] AudioBuffer;
    }
    AudioBuffer=new unsigned char[Length];
  }

  dst=(unsigned char*)AudioBuffer;
  src=(unsigned char*)AudioSampleArray;

  for(i=0;i<Length;i+=2){
    dst[i]=src[2*i+2];
    dst[i+1]=src[2*i+3];
  }
  
  ((MainWindow*)mainwindow)->audioOutput->Input(dst,Length);
}

void NES::AudioDataOutput_32(void* AudioSampleArray,int Length){

  if(AudioBufferLength<Length){
    AudioBufferLength=Length;
    if(AudioBuffer!=nullptr){
      delete[] AudioBuffer;
    }
    AudioBuffer=new unsigned char[Length];
  }

  memcpy(AudioBuffer,AudioSampleArray,Length);
  
  ((MainWindow*)mainwindow)->audioOutput->Input(AudioBuffer,Length);
}

void NES::FrameOutput(unsigned char* ColorIndexArray){

  uint32_t* Frame=new uint32_t[256*240];

  videoOutputLock->lock();
  palette->ConvertFrame(Frame,ColorIndexArray,256*240);
  videoOutputLock->unlock();

  QMetaObject::invokeMethod((MainWindow*)mainwindow,"DisplayNESFrame",Qt::ConnectionType::QueuedConnection,Q_ARG(void*,(void*)Frame));

}

void NES::AudioOutput(int32_t* AudioSampleArray,int Length){

  audioOutputLock->lock();

  switch(NumOfSampleBit){
    case 8:
      AudioDataOutput_8(AudioSampleArray,Length*2*4);
      break;
    case 16:
      AudioDataOutput_16(AudioSampleArray,Length*2*4);
      break;
    case 32:
      AudioDataOutput_32(AudioSampleArray,Length*2*4);
      break;
    default:
      break;
  }

  audioOutputLock->unlock();

}

uint32_t NES::GetStandardControllerStatus(){
  MainWindow* mainwindow;
  
  mainwindow=(MainWindow*)this->mainwindow;

  return mainwindow->GetPadStatus();
}



